package com.rzl.expense.quartz.util;

import cn.hutool.core.collection.CollUtil;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * sftp客户
 * <p>
 * https://blog.csdn.net/lk1985021/article/details/127378880
 *
 * @author admin
 * @date 2023/08/09
 */
@Slf4j
@Component
public class SftpClient {
    private static final String SLASH = "/";
    private static final String DOT = ".";
    private static final String DOUBLE_DOT = "..";
    private ChannelSftp sftp = null;
    private Session sshSession = null;
    @Autowired
    SftpConfig sftpProperties;

    /**
     * 连接sftp服务器
     */
    private void connect() {
        log.info("ftp连接开始host=" + sftpProperties.getHost() + ",port" +
                sftpProperties.getPort() + ",username=" + sftpProperties.getUserName());
        JSch jsch = new JSch();
        try {
            jsch.getSession(sftpProperties.getUserName(),
                    sftpProperties.getHost(), sftpProperties.getPort());
            sshSession = jsch.getSession(sftpProperties.getUserName(),
                    sftpProperties.getHost(), sftpProperties.getPort());
            log.info("ftp--->Session created.");
            sshSession.setPassword(sftpProperties.getPassWord());
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(properties);
            sshSession.connect();
            log.info("ftp--->Session connected.");
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            log.info("Opening Channel.");
            sftp = (ChannelSftp) channel;
            log.info("ftp--->Connected to " + sftpProperties.getHost());
        } catch (JSchException e) {
            log.error("sftp connect异常", e);
        }
    }

    /**
     * 断开连接
     */
    private void disconnect() {
        if (this.sftp != null && this.sftp.isConnected()) {
            this.sftp.disconnect();
            this.sftp = null;
            log.info("sftp is closed already");
        }
        if (this.sshSession != null && this.sshSession.isConnected()) {
            this.sshSession.disconnect();
            this.sshSession = null;
            log.info("sshSession is closed already");
        }
    }

    /**
     * 下载单个文件
     */
    public byte[] downloadFile(String filePath) {
        log.info("sftp下载文件 filePath{} 开始", filePath);
        connect();
        try (
                InputStream inputStream = sftp.get(filePath);
                ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        ) {
            //进入FTP服务器文件目录
            log.info("DownloadFile:{},success from sftp", filePath);
            log.info("ftp下载文件结束");
            byte[] buff = new byte[1024];
            int rc = 0;
            while ((rc = inputStream.read(buff, 0, 1024)) > 0) {
                swapStream.write(buff, 0, rc);
            }
            // in_b为转换之后的结果
            byte[] in_b = swapStream.toByteArray();
            return in_b;
        } catch (SftpException | IOException e) {
            log.error("sftp下载文件失败");
        } finally {
            disconnect();
        }
        return new byte[0];
    }

    /**
     * 上传文件
     *
     * @param inputStream    输入流
     * @param directory      目录
     * @param remoteFileName 远程文件名字
     */
    public void uploadFile(InputStream inputStream, String directory, String
            remoteFileName) {
        log.info("sftp 上传文件{} 开始", remoteFileName);
        connect();
        try {
            this.createDirs(directory, sftp);
            sftp.put(inputStream, remoteFileName);
        } catch (SftpException e) {
            log.error("sftp upload error", e);
        } finally {
            disconnect();
        }
    }

    /**
     * 列出文件
     *
     * @param directory 目录
     * @return 文件列表
     */
    public List<String> listFiles(String directory, String matchRule) {
        log.info("sftp listFiles 目录是:{} 开始,matchRule", directory);
        connect();
        try {
            Vector files = sftp.ls(directory);
            if (CollUtil.isNotEmpty(files)) {
                List<String> list = new ArrayList<>(files.size());
                for (Object object : files) {
                    ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) object;
                    log.info("fileName:{}", file.getFilename());
//文件名需包含后缀名以及排除 .和 ..
                    boolean match = file.getFilename().contains(matchRule);
                    if (!DOT.equals(file.getFilename()) &&
                            !DOUBLE_DOT.equals(file.getFilename())
                            && file.getFilename().contains(DOT) && match) {
                        String newFilePath = directory.endsWith(SLASH) ?
                                directory + file.getFilename() : directory + SLASH + file.getFilename();
                        list.add(newFilePath);
                    }
                }
                return list;
            }
        } catch (SftpException e) {
            log.error("sftp list files error", e);
        } finally {
            disconnect();
        }
        return Collections.emptyList();
    }

    /**
     * 列出文件
     *
     * @param directory 目录
     * @return 文件列表
     */
    public List<String> listFiles(String directory) {
        log.info("sftp listFiles 目录是:{} 开始,matchRule", directory);
        connect();
        try {
            Vector files = sftp.ls(directory);
            if (CollUtil.isNotEmpty(files)) {
                List<String> list = new ArrayList<>(files.size());
                for (Object object : files) {
                    ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) object;
                    log.info("fileName:{}", file.getFilename());
// 排除 .和 ..
                    if (!DOT.equals(file.getFilename()) &&
                            !DOUBLE_DOT.equals(file.getFilename())
                            && file.getFilename().contains(DOT)) {
                        String newFilePath = directory.endsWith(SLASH) ?
                                directory + file.getFilename() : directory + SLASH + file.getFilename();
                        list.add(newFilePath);
                    }
                }
                return list;
            }
        } catch (SftpException e) {
            log.error("sftp list files error", e);
        } finally {
            disconnect();
        }
        return Collections.emptyList();
    }

    /**
     * 创建SFTP目录，如果目录不存在就创建
     *
     * @param dirPath 源目录
     * @param sftp    sftp
     */
    private void createDirs(String dirPath, ChannelSftp sftp) {
        String[] dirs =
                Arrays.stream(dirPath.split("/")).filter(StringUtils::isNotBlank).toArray(String
                        []::new);
        for (String dir : dirs) {
            try {
                sftp.cd(dir);
                log.info("Change directory {}", dir);
            } catch (Exception e) {
                try {
                    sftp.mkdir(dir);
                    log.info("Create directory {}", dir);
                } catch (SftpException e1) {
                    log.error("Create directory failure, directory:{}", dir,e1);
                }
                try {
                    sftp.cd(dir);
                    log.info("Change directory {}", dir);
                } catch (SftpException e1) {
                    log.error("Change directory failure, directory:{}", dir,e1);
                }
            }
        }
    }


    public  InputStream getFileStream(String filename) {
        try {
            JSch jsch = new JSch();
            jsch.getSession(sftpProperties.getUserName(),
                    sftpProperties.getHost(), sftpProperties.getPort());
            sshSession = jsch.getSession(sftpProperties.getUserName(),
                    sftpProperties.getHost(), sftpProperties.getPort());
            log.info("ftp--->Session created.");
            sshSession.setPassword(sftpProperties.getPassWord());
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(properties);
            sshSession.connect();
            log.info("ftp--->Session connected.");

            ChannelSftp channelSftp = (ChannelSftp) sshSession.openChannel("sftp");
            channelSftp.connect();
            log.info("ftp--->Connected to " + sftpProperties.getHost());

            InputStream inputStream = channelSftp.get(filename);
            return inputStream;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
